using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public partial class FieldModel
{
  public class GridModel
  {
    private int[,] m_ptrGrids;
    private Dictionary<int, BallModel> m_ballMap = new Dictionary<int, BallModel>();
    private int m_nextAllocPtr;
    private Vector2Int m_size;
    public Vector2Int size => m_size;

    public GridModel(Vector2Int size) {
      Init(size);
    }

    public void CreateBallModelAt(BallInfo info, Vector2Int position) {
      var ptr = AllocBallAt(info, position);
      SetBallModel(position, ptr);
    }

    //空的球IsEmpty = true
    public BallModel GetBallModel(Vector2Int position) {
      var ptr = m_ptrGrids.SafeGet(position);
      var res = ptr == 0 ? BallModel.Empty : m_ballMap.SafeGet(ptr, BallModel.Empty);
      res.position = position;
      return res;
    }

    public BallModel GetBallModel(int x, int y) {
      return GetBallModel(new Vector2Int(x, y));
    }

    public BallModel GetBallModel(int ptr) {
      return m_ballMap.SafeGet(ptr, BallModel.Empty);
    }

    public void UpdateBallModel(int ptr, BallModel newModel) {
      if (!m_ballMap.ContainsKey(ptr))
      {
        Debug.LogError("[UpdateBallModel] ptr not exist: " + ptr);
        return;
      }
      newModel.ptr = ptr;
      m_ballMap[ptr] = newModel;
    }

    private void SetBallModel(Vector2Int position, int ptr) {
      if (!m_ballMap.ContainsKey(ptr) || !m_ptrGrids.SafeContains(position))
      {
        return;
      }
      var ballModel = m_ballMap[ptr];
      ballModel.position = position;
      ballModel.OnSetIntoField();
      m_ballMap[ptr] = ballModel;
      m_ptrGrids.TrySet(position, ptr);
    }

    public void EraseBallModel(Vector2Int position) {
      var ballModel = GetBallModel(position);
      if (ballModel.ptr != 0)
      {
        ballModel.OnRemoveFromField();
        m_ballMap[ballModel.ptr] = ballModel;
      }
      m_ptrGrids.TrySet(position, 0);
    }

    public void EraseBallModel(int x, int y) => EraseBallModel(new Vector2Int(x, y));

    public IEnumerable<BallModel> AllBallModels() {
      foreach (var pos in m_ptrGrids.AllPosition())
      {
        var ptr = m_ptrGrids[pos.x, pos.y];
        if (ptr != 0)
        {
          var ball = m_ballMap[ptr];
          ball.position = pos;
          yield return ball;
        }
      }
    }

    public IEnumerable<Vector2Int> AllPosition() {
      return m_ptrGrids.AllPosition();
    }

    public void SwapBall(Vector2Int pos0, Vector2Int pos1) {
      if (pos0 != pos1 && m_ptrGrids.SafeContains(pos0) && m_ptrGrids.SafeContains(pos1))
      {
        var tmp = m_ptrGrids[pos0.x, pos0.y];
        m_ptrGrids[pos0.x, pos0.y] = m_ptrGrids[pos1.x, pos1.y];
        m_ptrGrids[pos1.x, pos1.y] = tmp;

        var ptr0 = m_ptrGrids[pos0.x, pos0.y];
        if (ptr0 != 0)
        {
          var ball0 = m_ballMap[ptr0];
          ball0.position = pos0;
          m_ballMap[ptr0] = ball0;
        }

        var ptr1 = m_ptrGrids[pos1.x, pos1.y];
        if (ptr1 != 0)
        {
          var ball1 = m_ballMap[ptr1];
          ball1.position = pos1;
          m_ballMap[ptr1] = ball1;
        }
      }
    }

    public GridModel GetCopy() {
      var instance = new GridModel(size);
      instance.m_size = m_size;
      instance.m_nextAllocPtr = m_nextAllocPtr;
      instance.m_ptrGrids = m_ptrGrids.Clone() as int[,];
      instance.m_ballMap = new Dictionary<int, BallModel>(m_ballMap);
      return instance;
    }

    private void Init(Vector2Int size) {
      m_size = size;
      m_ptrGrids = new int[size.x, size.y];
    }

    private int AllocBallAt(BallInfo ballInfo, Vector2Int position) {
      var ball = BallModel.Create(ballInfo);
      ball.position = position;
      var ptr = ++m_nextAllocPtr;
      ball.ptr = ptr;
      m_ballMap[ptr] = ball;
      return ptr;
    }
  }
}